rebase 比较复杂, 应该是 git 中的高级应用了, 所以单独拿出来做篇 wiki, 转自: ihower blog.

使用 git rebase 避免無謂的 merge

git pull 預設的行為是將遠端的 repo. 與本地的 repo. 合併, 這也是 DVCS 的初衷, 將兩個 branch 合併. 但是, 很多時候會發生以下這種情形:

這是因為, 我們團隊的開發模式是本地的 branch 和遠端的 branch 會同步地非常頻繁(通常就是同名稱的 branch, 例如 master), 這兩個 branch 幾乎是完全同步. 這時候就會發現這些 merge 動作其實沒有必要, 會造成線圖無謂的複雜. 這時候, 會推薦使用以下這個指令:

git pull --`rebase`



加上 rebase 的意思是:

  1. 把本地 repo. 從上次 pull 之後的變更暫存起來
  2. 回復到上次 pull 時的情況
  3. 套用遠端的變更
  4. 最後再套用剛暫存下來的本地變更

畫圖說明一下好了, 假設合併前是這樣:

      D---E master
     /
A---B---C---F origin/master



使用 merge 合併後:

      D--------E
     /          \
A---B---C---F----G   master, origin/master



如果是 rebase 的方式, 就不會有 G 合併點:

A---B---C---F---D'---E'   master, origin/master



注意到, 其中 D’, E’ 的 commit SHA 序號跟本來 D, E 是不同的, 因為算是砍掉重新 commit 了.

你會問說, 有 conflict 怎麼辦?

rebasemerge 類似, 出現 conflict 一會暫停 rebase 動作, 需要你手動修復後, 然後才可以繼續動作. 這也是 rebasemerge 複雜一點的地方:

所以到底何時該用 merge? 何時可以 rebase? 你可能心理也有答案了, 如果你修改比較多, 預期會有較多的 conflict, 建議用 merge (不過, 如果是多次大範圍的主題式修改, 那是不是應該一開始就多開一個 branch 來做呢?). 如果修改範圍較小, 不太預期有 conflict, 則建議可以加上 rebase 參數.

如果想要把 rebase 當做 git pull 的預設值, 可以:

In git >= 1.7.9:
git config --global/local pull.rebase true

In git < 1.7.9:
git config --global/local branch.autosetuprebase always


重建 commit

rebase 的真正潛力是, 我們可以從指定的版號之後, 重新隨你意 commit 一次來重建 history, 超威的. 首先輸入 git rebase -i 版號 就會可以跳出 editor 可以編輯, 我們可以:

  1. 變更 commit 順序
  2. 將多個 commit 合併 squash
  3. 將一個 commit 打散 (edit 會停著讓你可以 git reset HEAD^ 打散重新 commit, 完成後 git rebase –continue)

另一種 rebase 用法是不需要打 -i, 直接指定另一個 branch 或 tag, 這樣就會重新 commit 另一個 branch 的東西, 然後才 commit 自己的 (也就是上面那段的用法).

git rebase 若有 conflict 就會停下來, 跟 merge 一樣處理完 add, 然後 git rebase –continue 就會繼續 commit (也可以 –skip–abort 放棄啦)

rebase 有個 onto 參數用法, 使用的情境是: 假設你有三個有 dependency 的 branch 分別叫做 master/contact/search, 後來發現 search branch 只有 depend on matser, 於是你可以輸入 git rebase –onto master contact search 這樣就會讓 search branch 從 master 的地方開始重新 commit.

再次提醒, rebase 千萬只能適合東西還沒 push 的情境, 或是你自己的 local 專用私人 branch. rebase 一個已經 push 出去的 repository, 然後你又把修改的 history push 出去, 是會造成超級大災難的.

在學習 rebase 的過程中, 很容易拿來跟 merge 比較一下. 我發現一個有趣的不同點: 如果有檔案在要被 merged 的 branch 中被刪除, 如果用 rebase 檔案最後會不存在, 但是用 merge 的話檔案最後還在.

另外, 在 rebase branch 之後, 如果再做 merge, 就會發現因為 master 直接就是被 merge 的祖先, 所以線圖直接變成一條線, 而有這種 parent 關係的 merge 就叫做 fast-forward. 換句話說, 因為沒有發生任何 merge commit, 也不會發生 conflict, Git 內部單純只是變更 reference 參照, 所以謂之 fast-forward.

好心提醒, 因為開 local branch 是如此便宜無害, 所以要做 rebase 時建議您可以先開一個 local branch 來實驗 rebase. 老實說, rebase 還挺危險的.

最後, rebase 我認為算是 Git 初學者最難理解的功能吧, 但是如果不知道什麼是 rebase, 就不能說是懂 Git 啊.